import axios from "axios";
import objectGet from "lodash/get";
import track, { setBeginTime } from "./interceptors/track";
import adapterFn from "./adapter";

/**
 *
 * @param {object} options
 * - {string} baseURL: 地址前缀
 * - {number} timeout: 超时时间
 * - {boolean} logApiError: 接口报错时是否上报
 * - {boolean} loginaddr: token失效时的跳转地址
 * - {function} enhanceHeaders: 对headers的补充
 * - {array} cachePair: 缓存接口对
 * - {string} cachePair[].get: 请求的接口
 * - {string} cachePair[].set: 修改的接口
 * - {object} listeners: 回调
 * - {function} listeners.tokenInvalid(data): token失效时的回调
 * - {function} listeners.success(result, headers): 接口返回成功时的回调
 * - {function} listeners.authDeny(result, headers): 接口无权限访问时的回调
 * - {function} listeners.error(statusCode): 排除以上情况下，其他所有接口故障的回调
 */
export default ({ Toast, loading }) => {
  return function service(optionsParam) {
    const defaultOptions = {
      baseURL: "",
      timeout: 1500,
      logApiError: true,
      loginaddr: "",
      enhanceHeaders() {},
      listeners: {
        tokenInvalid() {},
        success() {},
      },
      getToken() {
        return localStorage.getItem("token");
      },
    };
    const options = { ...defaultOptions, ...optionsParam };
    const adapter = adapterFn(options);
    const serviceInstance = axios.create({
      baseURL: options.baseURL,
      timeout: options.timeout,
      adapter,
    });
    /**
     * 与axios请求无关的配置，不会带给服务端的。写在`config`的`_config`属性里面。
     * `config`包含下列属性：
     * - noLoading: 请求过程中不显示loading图标
     * - noToast: 请求失败后不显示错误提醒
     * - ignoreToken: 该接口的headers不需要携带token
     */
    // --------- request 请求拦截器 ---------
    if (options.logApiError) {
      instance.interceptors.request.use(setBeginTime);
    }
    instance.interceptors.request.use((config) => {
      const { _config } = config;
      config.data = config.data || {};
      let url = config.url;
      url = url.includes("http") ? url : config.baseURL + url;
      // 对headers的补充，例如加上加密数据
      const headers = options.enhanceHeaders(config.data, url);
      // token可以是接口独有的，或者全局通用的。
      if (!_config.ignoreToken) {
        headers.token =
          (config.headers && config.headers.token) || options.getToken();
      }
      Object.assign(config.headers, headers);
      // 控制loading的显示
      if (!_config) {
        loading.open();
      } else if (!_config.noLoading) {
        loading.open(_config.loadingText || "");
      }
      return config;
    });
    // response 请求拦截器
    // 优先对token失效状态码进行处理
    instance.interceptors.response.use(
      (res) => {
        const {
          config: { _config },
        } = res;
        if (!_config || !_config.noLoading) {
          loading.close();
        }
        // 处理token失效，未登录状态的
        if (res.data.code === statusMap.NOTLOGIN) {
          options.listeners &&
            typeof options.listeners.tokenInvalid === "function" &&
            options.listeners.tokenInvalid(res.data);
          if (isProd() && options.loginaddr) {
            location.href = options.loginaddr;
          }
        }
        return res;
      },
      (err) => {
        // 处理接口故障，包括无数据返回，或者http code !== 200的
        if (err.response) {
          const { config, status } = err.response;
          if (!config || !config._config || !config._config.noLoading) {
            loading.close();
            options.listeners &&
              typeof options.listeners.error === "function" &&
              options.listeners.error(status);
          }
        }
        return Promise.reject(err);
      }
    );
    // 然后是埋点处理
    if (options.logApiError) {
      instance.interceptors.response.use(track("resolve"), track("reject"));
    }
    // 最后再处理弹窗显示
    instance.interceptors.response.use(
      (res) => {
        const { _config } = res.config;
        // http code为200，但是没有任何数据返回的。
        if (!res.data) {
          Toast({
            type: "error",
            message: "网络错误，请重试",
            duration: 1500,
          });
          return Promise.reject("网络错误，请重试");
        }
        // 未登录的处理过了，就直接返回
        if (res.data.code === statusMap.NOTLOGIN) {
          return Promise.reject(res.data.msg, res.data);
        }
        // 店客多项目正常的接口都会返回result字段，如果没有的话就是接口有问题。也有可能是接口http result code就不是200
        if (res.data && "result" in res.data === false) {
          if (res.data.code === statusMap.AUTH_DENY) {
            options.listeners &&
              typeof options.listeners.authDeny === "function" &&
              options.authDeny(res.data);
            return Promise.reject(res.data.msg, res.data);
          }
          console.log("接口没有返回result字段");
          Toast({
            type: "error",
            message: "网络错误，请重试",
            duration: 1500,
          });
          return Promise.reject("接口没有返回result字段");
        }
        if (res.data.code === statusMap.AUTH_DENY) {
          options.listeners &&
            typeof options.listeners.authDeny === "function" &&
            options.authDeny(res.data);
          return Promise.reject(res.data.msg, res.data);
        }
        if (res.data.code === statusMap.SUCCESS) {
          if (typeof options.resolve === "function") {
            options.listeners &&
              typeof options.listeners.success === "function" &&
              options.success(res.data.result, res.config.headers);
          }
          return res.data.result;
        }
        if (!_config || !_config.noToast) {
          Toast({
            type: "error",
            message: res.data.msg,
            duration: 1500,
          });
        }
        return Promise.reject(res.data);
      },
      (err) => {
        if (objectGet(err, "response.config")) {
          if (err.message.endsWith("code 404")) {
            console.log(`接口 %c${err.config.url}%c 不存在`, "color:red", "");
          }
          const { _config } = err.response.config;
          if (!_config || !_config.noToast) {
            Toast({
              type: "error",
              message: "网络错误，请重试",
            });
          }
        } else {
          if (err) {
            if (err.code === "ERR_NETWORK") {
              console.log(`${err.config.url} 接口不存在或跨域`);
            } else if (err.message && err.message.startsWith("timeout")) {
              console.log(`%c${err.config.url}%c 接口超时`, "color:red", "");
              loading.close();
            }
            if (err.data && err.data.code !== statusMap.NOTLOGIN) {
              Toast({
                type: "error",
                message: "网络错误，请重试",
              });
            }
          } else if (!err || !err.data) {
            Toast({
              type: "error",
              message: "网络错误，请重试",
            });
          }
        }
        return Promise.reject(err);
      }
    );
    return serviceInstance;
  };
};
